home *** CD-ROM | disk | FTP | other *** search
/ Hardcore Gamer Resource Kit / Hardcore Gamer Resource Kit - Disc 2.iso / Utils / UNIX / UNZIP520 / WINGUI / STATUS.C < prev    next >
C/C++ Source or Header  |  1996-01-23  |  29KB  |  842 lines

  1. /* Status.c -- the status module of WizUnZip
  2.  * Robert Heath. 1991.
  3.  *
  4.  * Modifications: 1995, Mike White
  5.  */
  6.  
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #include <time.h>
  10. #include <string.h>
  11. #include <ctype.h>
  12. #include <io.h>
  13. #include <stdio.h>
  14. #include <stdarg.h>
  15.  
  16. #include "wingui\wizunzip.h"
  17.  
  18. #define UNZIP_INTERNAL
  19. #include "unzip.h"
  20.  
  21. #define STATUS_INCREMENT    512 /* incremental status data size     */
  22. #define MAX_H_CHARS 160         /* max horizontal chars.            */
  23. #define MAX_INDEX_ENTRIES 16    /* Message Window index max entries */
  24. #define cchBufferMax 0xffffL    /* max Message Buffer size. Must fit
  25.                                  * within one memory segment!       */
  26. #define cchTextOutMax 0x7fffL /* max no. bytes TextOut() accepts */
  27. #define STDIO_BUF_SIZE (FILNAMSIZ+LONG_FORM_FNAME_INX) /* buffer size during printf or fprintf */
  28.  
  29. static short yClient;               /* height of client area */
  30. static short nVscrollPos = 0;       /* scroll position of mesg. window  */
  31. static short nNumLines = 0;         /* number of lines in buffer */
  32. static short nVscrollMax;           /* max scroll position of mesg. window  */
  33. static DWORD dwStatusSize = 0L;     /* status data size */
  34. static DWORD dwBufferSize = 0L;     /* Status buffer size.  Never
  35.                                        exceeds cchBufferMax     */
  36. static HANDLE hStatusBuffer = NULL;        /* global mesg. handle  */
  37. static DWORD rgidwMsgWin[MAX_INDEX_ENTRIES]; /* max index entries   */
  38. static short    cMsgWinEntries;         /* no. active index entries, with
  39.                                        MAX_INDEX_ENTRIES as its max.
  40.                                        When set to 0, it's time to
  41.                                        re-index.*/
  42. static short nLinesPerEntry;        /* lines per index entry */
  43.  
  44. /* displayed when buffer shouldn't grow or can't grow */
  45. static char __based(__segname("STRINGS_TEXT")) szClearBufferMsg[] =
  46.             "Clearing Status window to make room for more information.";
  47. static char __based(__segname("STRINGS_TEXT")) szCantClipboard[] =
  48.             "Cannot get enough memory to copy Status window to clipboard.";
  49.  
  50. struct KeyEntry
  51. {
  52.     WORD    wVirtKey;
  53.     BOOL    bCntl;
  54.     WORD    wMessage;
  55.     WORD    wRequest;
  56. } __based(__segname("STRINGS_TEXT")) KeyTable[] =
  57. {
  58.     /* vertical scroll control */
  59.     {VK_HOME,   TRUE,   WM_VSCROLL, SB_TOP },
  60.     {VK_END,    TRUE,   WM_VSCROLL, SB_BOTTOM },
  61.     {VK_PRIOR,  FALSE,  WM_VSCROLL, SB_PAGEUP },
  62.     {VK_NEXT,   FALSE,  WM_VSCROLL, SB_PAGEDOWN },
  63.     {VK_UP,     FALSE,  WM_VSCROLL, SB_LINEUP },
  64.     {VK_DOWN,   FALSE,  WM_VSCROLL, SB_LINEDOWN },
  65.  
  66.     /* horizontal scroll control */
  67.     {VK_HOME,   FALSE,  WM_HSCROLL, SB_TOP },
  68.     {VK_END,    FALSE,  WM_HSCROLL, SB_BOTTOM },
  69.     {VK_PRIOR,  TRUE,   WM_HSCROLL, SB_PAGEUP },
  70.     {VK_NEXT,   TRUE,   WM_HSCROLL, SB_PAGEDOWN },
  71.     {VK_LEFT,   FALSE,  WM_HSCROLL, SB_LINEUP },
  72.     {VK_RIGHT,  FALSE,  WM_HSCROLL, SB_LINEDOWN },
  73. } ;
  74.  
  75. #define NUMKEYS (sizeof(KeyTable)/sizeof(struct KeyEntry))
  76.  
  77. /* Forward Refs
  78.  */
  79. static void FreeStatusLog(void);
  80.  
  81. /* Globals */
  82. BOOL bRealTimeMsgUpdate = TRUE; /* update messages window in real-time.
  83.                                  * Reset by callers when update can be
  84.                                  * be deferred.
  85.                                  */
  86. BOOL gfCancelDisplay = FALSE;   /* cancel display if got in over our heads */
  87.  
  88. /* Clears status buffer. Frees buffer.
  89.  */
  90. static void FreeStatusLog(void)
  91. {
  92. if (hStatusBuffer)
  93.    {
  94.    GlobalFree(hStatusBuffer);
  95.    hStatusBuffer = (HANDLE)0;
  96.    }
  97. dwStatusSize = 0L;      /* status data size             */
  98. dwBufferSize = 0L;      /* status buffer size           */
  99. nNumLines = 0;          /* number of lines in buffer    */
  100. nVscrollMax = 1;
  101. SetScrollRange(hWndStatus, SB_VERT, 0, 1, FALSE);
  102. nVscrollPos = 0;
  103. SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  104. }
  105.  
  106. /* Update Message Window Position is called after adding
  107.  * a number of lines to the message window without updating it.
  108.  * The function invalidates then updates the window.
  109.  */
  110. void UpdateMsgWndPos(void)
  111. {
  112. #ifdef __BORLANDC__
  113. #pragma warn -ccc
  114. #endif
  115. nVscrollPos = (short)max(0,(nNumLines-cLinesMessageWin+1));     /* set position to next to last line   */
  116. #ifdef __BORLANDC__
  117. #pragma warn .ccc
  118. #endif
  119. if (nVscrollPos < 0)
  120.    nVscrollPos = 0;
  121. SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  122. InvalidateRect(hWndStatus, NULL, TRUE);
  123. UpdateWindow(hWndStatus);
  124. }
  125.  
  126. /* Set Status Top Window Position is called after adding
  127.  * a number of lines to the message window without updating it.
  128.  * Unlike UpdateMsgWndPos() above, this function sets the
  129.  * status window position to the top.
  130.  * The function invalidates then updates the window.
  131.  */
  132. void SetStatusTopWndPos(void)
  133. {
  134. nVscrollPos = 0;     /* set position to next to top line   */
  135. SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  136. InvalidateRect(hWndStatus, NULL, TRUE);
  137. UpdateWindow(hWndStatus);
  138. }
  139.  
  140. /* Add message line (or part of a line) to the global status buffer
  141.  * that is the contents of the Message Window.
  142.  * Assumes that global data is unlocked when called.
  143.  */
  144. void WriteStringToMsgWin(PSTR psz, BOOL bUpdate)
  145. {
  146. WriteBufferToMsgWin(psz, strlen(psz), bUpdate);
  147. }
  148.  
  149. /* Add message buffer (maybe part of a line) to the global status buffer
  150.  * that is the contents of the Message Window.
  151.  * Assumes that global data is unlocked when called.
  152.  */
  153. void WriteBufferToMsgWin(LPSTR pszBuffer, int nBufferLen, BOOL bUpdate)
  154. {
  155. LPSTR   lpszT;                   /* pointer into buffer */
  156. HANDLE hStatusBufferTmp;
  157. LPSTR lpGlobalBuffer;            /* pointer into global buffer */
  158. DWORD dwNewSize = dwStatusSize + (DWORD)nBufferLen;
  159. int nIncrLines = 0;              /* incremental lines in buffer */
  160. int nIncompleteExistingLine = 0; /* add -1 if incomplete existing last line */
  161. int nIncompleteAddedLine = 0;    /* add +1 if incomplete added last line */
  162. DWORD dwRequestedSize;           /* Size needed to hold all data. Can't
  163.                                     practically exceeded cchBufferMax.*/
  164.  
  165. if (gfCancelDisplay)      /* if canceling display (in the middle of a lengthy operation) */
  166.    return;               /* just discard data */
  167.  
  168. if (!nBufferLen)    /* if no data */
  169.    return;         /* just beat it */
  170.  
  171. /* count LF's in buffer to later add to total */
  172. for (lpszT = pszBuffer;
  173.    lpszT != NULL && (lpszT - pszBuffer) < nBufferLen; )
  174.    {
  175.    /* use memchr() for speed (?) considerations */
  176. #ifndef WIN32   
  177.    if ((lpszT = _fmemchr(lpszT, '\n',
  178.       (size_t)(nBufferLen - (lpszT - pszBuffer))))!=0)
  179. #else
  180.    if ((lpszT = memchr(lpszT, '\n',
  181.       (size_t)(nBufferLen - (lpszT - pszBuffer))))!=0)
  182. #endif
  183.       {
  184.       nIncrLines++;   /* tally line found */
  185.       lpszT++;        /* point beyond LF for next pass */
  186.       }
  187.    }
  188. if (dwNewSize > dwBufferSize)   /* if won't fit or 1st time */
  189.    {
  190.    /* Round up if necessary to nearest whole increment */
  191.    dwRequestedSize = ((dwNewSize + STATUS_INCREMENT - 1) /
  192.                         STATUS_INCREMENT) * STATUS_INCREMENT;
  193.    if (hStatusBuffer)  /* if buffer exists, realloc */
  194.       {
  195.       if (dwRequestedSize <= cchBufferMax &&
  196.          ((hStatusBufferTmp = GlobalReAlloc(hStatusBuffer,
  197.          dwRequestedSize, GMEM_MOVEABLE))!=0))
  198.          {
  199.          /* successful re-allocation */
  200.          hStatusBuffer = hStatusBufferTmp;
  201.          dwBufferSize = dwRequestedSize;
  202.          }
  203.       else /* re-allocation failed, make last-ditch attempt! */
  204.          {
  205.          /* Here's where the message is generated when a display
  206.             is done that fills up the list box requiring us to
  207.             clear the window entirely to display any more.
  208.          */
  209.          FreeStatusLog();        /* free own buffers */
  210.          if (uf.fAutoClearDisplay == FALSE)
  211.             {
  212.             if (MessageBox (hWndMain, szClearBufferMsg,
  213.                "Note", MB_ICONINFORMATION | MB_OKCANCEL) == IDCANCEL)
  214.                gfCancelDisplay = TRUE;
  215.  
  216.             else /* no cancel */
  217.                WriteBufferToMsgWin(pszBuffer, nBufferLen, bUpdate);
  218.             return;
  219.             }
  220.          else
  221.             {
  222.             WriteBufferToMsgWin(pszBuffer, nBufferLen, bUpdate);
  223.             return;
  224.             }
  225.          }
  226.       }
  227.    else    /* 1st time */
  228.       {
  229.       if ((hStatusBuffer = GlobalAlloc(GMEM_MOVEABLE,
  230.          dwRequestedSize))!=0)
  231.          {
  232.          dwBufferSize = dwRequestedSize; /* save it      */
  233.          }
  234.       else    /* 1st allocation failed! */
  235.          {
  236.          WinAssert(hStatusBuffer);
  237.          return;
  238.          }
  239.       }
  240.    }
  241.     /* should be easy copy of data from here */
  242. lpGlobalBuffer = GlobalLock(hStatusBuffer);
  243. if (lpGlobalBuffer)
  244.    {
  245.    /* Account for partial lines existing and being added. */
  246.    if (dwStatusSize  &&
  247.       lpGlobalBuffer[(int)dwStatusSize-1] != '\n')
  248.       nIncompleteExistingLine-- ; /* subtract 1 */
  249.  
  250.       if (pszBuffer[nBufferLen-1] != '\n') /* nBufferLen guaranteed >0 */
  251.          nIncompleteAddedLine++ ;  /* add 1 */
  252.  
  253.         /* copy data into global buffer */
  254.       if (nBufferLen)   /* map to ANSI; if 0 don't copy; 0 means 65K  */
  255.          {
  256.          OemToAnsiBuff(pszBuffer, &lpGlobalBuffer[(int)dwStatusSize], nBufferLen);
  257.          }
  258.         /* bump no. lines accounting for incomplete lines */
  259.       nNumLines += (short)(nIncrLines+nIncompleteExistingLine+nIncompleteAddedLine);
  260.       dwStatusSize = dwNewSize;       /* new data size counting end null  */
  261.       GlobalUnlock(hStatusBuffer);
  262.       nVscrollMax = (short)max(1, nNumLines + 2 - yClient/dyChar);
  263.       SetScrollRange(hWndStatus, SB_VERT, 0, nVscrollMax, FALSE);
  264.       cMsgWinEntries = 0; /* re-index whenever more data is added */
  265.       if (bUpdate)        /* if requested to update message box */
  266.          {
  267. #ifdef __BORLANDC__
  268. #pragma warn -ccc
  269. #endif
  270.          nVscrollPos = (short)max(0,(nNumLines-cLinesMessageWin+1));     /* set position to next to last line   */
  271. #ifdef __BORLANDC__
  272. #pragma warn .ccc
  273. #endif
  274.          if (nVscrollPos < 0)
  275.             nVscrollPos = 0;
  276.          SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  277.          InvalidateRect(hWndStatus, NULL, TRUE);
  278.          UpdateWindow(hWndStatus);
  279.          }
  280.       }
  281.    else
  282.       {
  283.       WinAssert(lpGlobalBuffer);
  284.       }
  285. EnableWindow(hCopyStatus, TRUE);
  286. EnableMenuItem(hMenu, IDM_COPY, MF_ENABLED);
  287. }
  288.  
  289. long WINAPI StatusProc(HWND hWnd, WORD wMessage, WPARAM wParam, LPARAM lParam)
  290. {
  291. short xClient ;        /* size of client area  */
  292. HDC     hDC;           /* device context */
  293. PAINTSTRUCT ps;
  294. struct KeyEntry __far *pKE;     /* pointer to key entry */
  295. LPSTR   lpStatusBuffer;/* pointer to global msg. buffer */
  296. int     nMenuItemCount;/* no. items in System menu before deleting separators */
  297. BOOL    bCntl;         /* control shift pressed ? */
  298. static short nHscrollMax;
  299. static short nHscrollPos;
  300. static short nMaxWidth;/* in pixels */
  301. short nVscrollInc;
  302. short nHscrollInc;
  303. short i, x, y, nPaintBeg, nPaintEnd;
  304. HMENU   hSysMenu;           /* this guy's system menu */
  305.  
  306. switch (wMessage)
  307.    {
  308.    case WM_CREATE:
  309.       nMaxWidth = (short) (MAX_H_CHARS * dxChar);
  310.       nVscrollPos = 0;
  311.       nVscrollMax = (short)max(1,nNumLines);
  312.       SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
  313.       SetScrollPos(hWnd, SB_VERT, 0, TRUE);
  314.  
  315.       /* Remove system menu items to limit user actions on status window */
  316.       hSysMenu = GetSystemMenu(hWnd, FALSE);
  317.       DeleteMenu(hSysMenu, SC_SIZE, MF_BYCOMMAND);
  318.       DeleteMenu(hSysMenu, SC_MOVE, MF_BYCOMMAND);
  319.       DeleteMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
  320.       DeleteMenu(hSysMenu, SC_TASKLIST, MF_BYCOMMAND);
  321.  
  322.       /* walk thru menu and delete all separator bars */
  323.       for (nMenuItemCount = GetMenuItemCount(hMenu);
  324.          nMenuItemCount ; nMenuItemCount--)
  325.          {
  326.          if (GetMenuState(hSysMenu, nMenuItemCount-1, MF_BYPOSITION) & MF_SEPARATOR)
  327.             {
  328.             DeleteMenu(hSysMenu, nMenuItemCount-1, MF_BYPOSITION);
  329.             }
  330.          }
  331.       return 0;
  332.  
  333.    case WM_SIZE:
  334.       xClient = LOWORD(lParam);/* x size of client area */
  335.       yClient = HIWORD(lParam);/* y size of client area */
  336.  
  337.       nVscrollMax = (short)max(1, nNumLines + 2 - yClient/dyChar);
  338.       nVscrollPos = min(nVscrollPos, nVscrollMax);
  339.  
  340.       SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
  341.       SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  342.  
  343.       nHscrollMax = (short)max(0, 2 + (nMaxWidth - xClient) / dxChar);
  344.       nHscrollPos = min(nHscrollPos, nHscrollMax);
  345.  
  346.       SetScrollRange(hWnd, SB_HORZ, 0, nHscrollMax, FALSE);
  347.       SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
  348.  
  349.       return 0;
  350.  
  351.    case WM_SYSCOMMAND:
  352.       switch ((wParam & 0xFFF0))
  353.          {
  354.          case SC_RESTORE:    /* alert parent         */
  355.             PostMessage(hWndMain, WM_COMMAND, IDM_SPLIT, 0L);
  356.             break;
  357.          case SC_MAXIMIZE:
  358.             PostMessage(hWndMain, WM_COMMAND, IDM_MAX_STATUS, 0L);
  359.             break;
  360.          default:
  361.             return DefWindowProc(hWnd, wMessage, wParam, lParam);
  362.          }
  363.       break;
  364.  
  365.    case WM_COMMAND:
  366.       if (LOWORD(wParam) == IDM_CLEAR_STATUS)
  367.          {
  368.          FreeStatusLog();
  369.          InvalidateRect(hWnd, NULL, TRUE);
  370.          UpdateWindow(hWnd);
  371.          }
  372.       break;
  373.    case WM_VSCROLL:    /* scroll bar action on list box */
  374.       switch (LOWORD(wParam))
  375.          {
  376.          case SB_TOP:
  377.             nVscrollInc = (short)-nVscrollPos;
  378.             break;
  379.          case SB_BOTTOM:
  380.             nVscrollInc = (short)(nVscrollMax - nVscrollPos);
  381.             break;
  382.          case SB_LINEUP:
  383.             nVscrollInc = -1;
  384.             break;
  385.          case SB_LINEDOWN:
  386.             nVscrollInc = 1;
  387.             break;
  388.          case SB_PAGEUP:
  389.             nVscrollInc = (short)min(-1, -yClient/dyChar);
  390.             break;
  391.          case SB_PAGEDOWN:
  392.             nVscrollInc = (short)max(1, yClient/dyChar);
  393.             break;
  394.          case SB_THUMBPOSITION:
  395.             nVscrollInc = (short)(LOWORD(lParam) - nVscrollPos);
  396.             break;
  397.          default:    /* END_SCROLL comes thru here */
  398.             nVscrollInc = 0;
  399.          }
  400.  
  401.       if ((nVscrollInc = (short)max(-nVscrollPos,
  402.          min(nVscrollInc, nVscrollMax - nVscrollPos)))!=0)
  403.          {
  404.          nVscrollPos += nVscrollInc;
  405.          ScrollWindow(hWnd, 0, -dyChar * nVscrollInc, NULL, NULL);
  406.          SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  407.          UpdateWindow(hWnd);
  408.          }
  409.       return 0;
  410.  
  411.    case WM_HSCROLL:    /* scroll bar action on list box */
  412.         switch (LOWORD(wParam))
  413.         {
  414.         case SB_TOP:
  415.             nHscrollInc = (short)-nHscrollPos;
  416.             break;
  417.         case SB_BOTTOM:
  418.             nHscrollInc = (short)(nHscrollMax - nHscrollPos);
  419.             break;
  420.         case SB_LINEUP:
  421.             nHscrollInc = -1;
  422.             break;
  423.         case SB_LINEDOWN:
  424.             nHscrollInc = 1;
  425.             break;
  426.         case SB_PAGEUP:
  427.             nHscrollInc = -8;
  428.             break;
  429.         case SB_PAGEDOWN:
  430.             nHscrollInc = 8;
  431.             break;
  432.         case SB_THUMBPOSITION:
  433.             nHscrollInc = (short)(LOWORD(lParam) - nHscrollPos);
  434.             break;
  435.         default:
  436.             return DefWindowProc(hWnd, wMessage, wParam, lParam);
  437.         }
  438.  
  439.         if ((nHscrollInc = (short)max(-nHscrollPos,
  440.             min(nHscrollInc, nHscrollMax - nHscrollPos)))!= (short)0)
  441.         {
  442.             nHscrollPos += nHscrollInc;
  443.             ScrollWindow(hWnd, -dxChar * nHscrollInc, 0, NULL, NULL);
  444.             SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
  445.         }
  446.         return 0;
  447.  
  448.     case WM_KEYDOWN:
  449.         bCntl = (BOOL)(GetKeyState(VK_CONTROL) < 0 ? TRUE : FALSE);
  450.         for (i = 0, pKE = KeyTable; i < NUMKEYS; i++, pKE++)
  451.         {
  452.             if ((wParam == pKE->wVirtKey) && (bCntl == pKE->bCntl))
  453.             {
  454.                 SendMessage(hWnd, pKE->wMessage, pKE->wRequest, 0L);
  455.                 break;
  456.             }
  457.         }
  458.         break;
  459.     case WM_PAINT:
  460.         if (!hStatusBuffer)         /* if nothing to paint  */
  461.            {
  462.            return DefWindowProc(hWnd, wMessage, wParam, lParam);
  463.            }
  464.         {
  465.         register LPSTR lpsz; /* current char */
  466.         LPSTR lpszNextLF;    /* address of next '\n' in buffer */
  467.         LPSTR lpszStart;     /* paint starting character */
  468.         LPSTR lpszLineCur;   /* beginning of current line */
  469.         HANDLE hNew;
  470.         DWORD   cchLine;     /* length of current line */
  471.         short  nLinesSinceLastEntry; /* lines since last entry */
  472.         DWORD  dwSearchLen;  /* length for _fmemchr() to search */
  473.  
  474.         lpszStart = NULL;    /* paint starting character */
  475.         lpStatusBuffer = GlobalLock(hStatusBuffer);
  476.         WinAssert(lpStatusBuffer);  /* DEBUG */
  477.         hDC = BeginPaint(hWnd, &ps);
  478.         WinAssert(hDC);             /* DEBUG */
  479.         hNew = SelectObject( hDC, hFixedFont);
  480.         WinAssert(hNew);
  481.         nPaintBeg = (short)max(0, nVscrollPos+ps.rcPaint.top/dyChar -1);
  482.         nPaintEnd = (short)min(nNumLines, nVscrollPos + ps.rcPaint.bottom/dyChar);
  483.         if (nPaintBeg >= nPaintEnd) /* if no painting to do ... */
  484.            {
  485.            EndPaint(hWnd, &ps);
  486.            GlobalUnlock(hStatusBuffer);
  487.            return 0;
  488.            }
  489.         if (!cMsgWinEntries)    /* re-index whenever more data is added */
  490.            {
  491.            /* Round up to make lines_per_entry encompass all
  492.             * possible lines in buffer.
  493.             */
  494.            nLinesPerEntry = (short)((nNumLines+MAX_INDEX_ENTRIES-1) / MAX_INDEX_ENTRIES);
  495.            if (!nLinesPerEntry)    /* if zero */
  496.               nLinesPerEntry++;   /* set to 1 as minimum */
  497.  
  498.            /* Count lines from beginning of buffer to:
  499.             * 1) mark beginning of paint sequence (lpszStart) and
  500.             * 2) periodically save buffer index in MsgWinIndex[] table.
  501.             */
  502.            for (lpsz = lpStatusBuffer, i = 0, nLinesSinceLastEntry = 0;
  503.               (DWORD)(lpsz - lpStatusBuffer) < dwStatusSize ; i++)
  504.               {
  505.               /* We are at the 1st character position in the line  */
  506.               if (i == nPaintBeg) /* Starting point for paint ? */
  507.                  lpszStart = lpsz;   /* If so, mark starting point */
  508.  
  509.               /* Entry time ? */
  510.               if (!nLinesSinceLastEntry++ &&
  511.                  cMsgWinEntries < MAX_INDEX_ENTRIES)
  512.                  {
  513.                  rgidwMsgWin[cMsgWinEntries] =
  514.                     (DWORD)(lpsz - lpStatusBuffer); /* save index */
  515.                  cMsgWinEntries++;
  516.                  }
  517.  
  518.               if (nLinesSinceLastEntry >= nLinesPerEntry)
  519.                  nLinesSinceLastEntry = 0;
  520.  
  521.               /* Use _fmemchr() to find next LF.
  522.                * It's probably optimized for searches.
  523.                */
  524.               dwSearchLen = dwStatusSize -
  525.                  (DWORD)(lpsz - lpStatusBuffer);
  526. #ifndef WIN32
  527.               if (((lpszNextLF = _fmemchr(lpsz, '\n', (size_t)dwSearchLen)))!=0)
  528. #else
  529.               if (((lpszNextLF = memchr(lpsz, '\n', (size_t)dwSearchLen)))!=0)
  530. #endif
  531.                  lpsz = ++lpszNextLF;    /* use next char as beg of line */
  532.               else /* use lpsz with incremented value */
  533.                  lpsz += (int)dwSearchLen;
  534.  
  535.               } /* bottom of still-counting-lines loop */
  536.  
  537.            WinAssert(lpszStart);
  538.            lpsz = lpszStart;       /* restore starting point */
  539.            WinAssert((DWORD)lpsz >= (DWORD)lpStatusBuffer &&
  540.               (DWORD)lpsz < (DWORD)&lpStatusBuffer[(int)dwStatusSize]);
  541.  
  542.            }   /* bottom of need-to-build-index block  */
  543.         else    /* index is still valid */
  544.            {
  545.            short nIndexEntry;
  546.  
  547.            /* Find index of line number which is equal to or just
  548.             * below the starting line to paint. Work backwards
  549.             * thru the table. Here, "i" is the line no. corresponding
  550.             * to the current table index.
  551.             */
  552.            for (nIndexEntry = (short)(cMsgWinEntries - 1),
  553.               i = (short)(nIndexEntry * nLinesPerEntry);
  554.               nIndexEntry >= 0 &&
  555.               nPaintBeg < i ;
  556.               nIndexEntry--, i -= nLinesPerEntry )
  557.               {
  558.               ;
  559.               }
  560.  
  561.            WinAssert(nIndexEntry >= 0);
  562.            WinAssert(i <= nPaintBeg);
  563.  
  564.            /* OK, we've got a head start on the search.
  565.             * Start checking characters from the position found
  566.             * in the index table.
  567.             */
  568.            for (lpsz = &lpStatusBuffer[(int)rgidwMsgWin[nIndexEntry]];
  569.               i < nPaintBeg &&
  570.               (DWORD)(lpsz - lpStatusBuffer) < dwStatusSize;
  571.               ++i)
  572.               {
  573.               /* Find length of current line.  Use _fmemchr() to
  574.                * find next LF.
  575.                */
  576.               dwSearchLen = dwStatusSize - (DWORD)(lpsz - lpStatusBuffer);
  577. #ifndef WIN32
  578.               if ((lpszNextLF = _fmemchr(lpsz, '\n', (size_t)dwSearchLen)) != NULL)
  579. #else
  580.               if ((lpszNextLF = memchr(lpsz, '\n', (size_t)dwSearchLen)) != NULL)
  581. #endif
  582.                  lpsz = ++lpszNextLF; /* point to next char. past '\n' */
  583.  
  584.               else /* If search fails, pretend LF exists, go past it. */
  585.                  lpsz += (int)dwSearchLen;
  586.  
  587.               }
  588.            } /* bottom of index-is-still-valid block. */
  589.  
  590.             /* At this point we've got the buffer address, lpsz, for the 1st
  591.              * line at which we begin painting, nPaintBeg.
  592.              */
  593.         for (i = nPaintBeg;
  594.              (i < nPaintEnd) &&
  595.              ((DWORD)lpsz  >= (DWORD)lpStatusBuffer) &&
  596.              ((DWORD)(lpsz  - lpStatusBuffer) < dwStatusSize) ;
  597.              ++i)
  598.              {
  599.              lpszLineCur = lpsz;
  600.              /* Find length of current line. Use _fmemchr() to find next LF.
  601.               */
  602.              dwSearchLen = dwStatusSize - (DWORD)(lpsz - lpStatusBuffer);
  603. #ifndef WIN32
  604.              if ((lpszNextLF = _fmemchr(lpsz, '\n', (size_t)dwSearchLen)) == NULL)
  605. #else
  606.              if ((lpszNextLF = memchr(lpsz, '\n', (size_t)dwSearchLen)) == NULL)
  607. #endif
  608.                 {
  609.                 /* If search fails, pretend we found LF, we won't
  610.                  * display it anyway.
  611.                  */
  612.                 lpszNextLF = lpsz + (int)dwSearchLen;
  613.                 }
  614.              WinAssert((DWORD)lpszNextLF >= (DWORD)lpszLineCur); /* should be non-negative   */
  615.              WinAssert((DWORD)lpszNextLF >= (DWORD)lpStatusBuffer && /* DEBUG */
  616.                 (DWORD)lpszNextLF <= (DWORD)&lpStatusBuffer[(int)dwStatusSize]);
  617.  
  618.              x = (short)(dxChar * (1 - nHscrollPos));
  619.              y = (short)(dyChar * (1 - nVscrollPos + i));
  620.              cchLine = (DWORD)(lpszNextLF - lpszLineCur);/* calc length*/
  621.              /* don't display '\r'   */
  622.              if (cchLine && lpszLineCur[(int)cchLine-1] == '\r')
  623.                 cchLine--;
  624.  
  625.              /* may be displaying long lines if binary file */
  626.              if (cchLine > cchTextOutMax)
  627.                 cchLine = cchTextOutMax;
  628.  
  629.              TabbedTextOut(hDC, x, y, lpszLineCur, (int)cchLine, 0, NULL, 0);
  630.              lpsz = ++lpszNextLF; /* point to char. past '\n' */
  631.             }
  632.             EndPaint(hWnd, &ps);
  633.             GlobalUnlock(hStatusBuffer);
  634.             return 0;
  635.         }
  636.     case WM_CLOSE:
  637.         DestroyWindow(hWnd);
  638.         break;
  639.     case WM_DESTROY:
  640.         FreeStatusLog();
  641.         break;
  642.     default:
  643.         return DefWindowProc(hWnd, wMessage, wParam, lParam);
  644.     }
  645.     return 0L;
  646. }
  647.  
  648. /* Map CR to CRLF is called by the printf and fprintf clones below
  649.  * to guarantee that lines added to the Status window will be
  650.  * properly terminated DOS-style in case they are copied
  651.  * to the Windows clipboard.
  652.  * This converts the buffer in-place, provided it won't
  653.  * grow beyond the original buffer allocation.
  654.  */
  655.  
  656. static void MapCRtoCRLF(PSTR pszOrigBuffer);
  657. static void MapCRtoCRLF(PSTR pszOrigBuffer)
  658. {
  659. PSTR pC, pDest;
  660. UINT cBufLen = 0;  // no. chars in buffer, including final null
  661. UINT cLFs = 0;    // no. LF's in string. \n is linefeed
  662.  
  663. for (pC = pszOrigBuffer; *pC; pC++)
  664.    {
  665.    cBufLen++;
  666.    if (*pC == '\n')   // found a linefeed
  667.       cLFs++;
  668.    }
  669. cBufLen++;   /* buffer length includes final null */
  670. pC = &pszOrigBuffer[cBufLen-1]; /* point to old end's null char */
  671. if (cBufLen + cLFs > STDIO_BUF_SIZE) /* room to add CR's ? */
  672.    return;             /* if not, don't bother */
  673.  
  674. /* copy data back-to-front, effectively inserting CR before LF */
  675. pDest = &pszOrigBuffer[cBufLen+cLFs-1]; /* point to new end */
  676. for  (; cBufLen; pC--, cBufLen--)
  677.    {
  678.    *pDest-- = *pC ;    /* copy data byte */
  679.    if ((*pC == '\n') && ((*(pC-1)) != '\r'))    /* was that a linefeed? */
  680.       *pDest-- = '\r'; /* if so, insert CR */
  681.    }
  682. }
  683.  
  684.  
  685. /* Printf buffers the current output and counts the number of lines
  686.  * within it.  It makes sure there is enough space in the global
  687.  * buffer, then copies the buffered data to the global buffer.
  688.  * It then triggers a repaint of the status buffer.
  689.  */
  690. int __far __cdecl printf(const char *format, ...)
  691. {
  692. va_list argptr;
  693. HANDLE hMemory;
  694. PSTR pszBuffer;
  695. int len;
  696.  
  697. va_start(argptr, format);
  698. hMemory = LocalAlloc(LMEM_MOVEABLE, STDIO_BUF_SIZE);
  699. WinAssert(hMemory);
  700. if (!hMemory)
  701.    {
  702.    return 0;
  703.    }
  704. pszBuffer = (PSTR)LocalLock(hMemory);
  705. WinAssert(pszBuffer);
  706. len = vsprintf(pszBuffer, format, argptr);
  707. va_end(argptr);
  708. WinAssert(strlen(pszBuffer) < STDIO_BUF_SIZE);  /* raise STDIO_BUF_SIZE ?   */
  709. MapCRtoCRLF(pszBuffer);   /*  map CR's to CRLF's */
  710. WriteStringToMsgWin(pszBuffer, bRealTimeMsgUpdate);
  711. LocalUnlock(hMemory);
  712. LocalFree(hMemory);
  713. /* MW: changed to return the actual number of bytes being printed */
  714. return len;
  715. }
  716.  
  717. #define FPRINTF_BUF_SIZE 16384
  718. #define MSG_WIN_BUF_SIZE 8192
  719.  
  720. int win_fprintf(FILE *file, unsigned int size, char *buf)
  721. {
  722. unsigned int len, count, p = 0;
  723. char * buffer;
  724. char * pbuf;
  725.  
  726. if ((file != stderr) && (file != stdout))
  727.    {
  728.    len = write(fileno(file),(char *)(buf),size);
  729.    return len;
  730.    }
  731. if (size < MSG_WIN_BUF_SIZE)
  732.    count = size;
  733. else
  734.    count = MSG_WIN_BUF_SIZE;
  735. pbuf = buf;
  736. /* Note the additional buffer size here (8K which is excessive,
  737.    but makes lots of room available for extra line feeds etc)
  738. */
  739. buffer = (char *)malloc(FPRINTF_BUF_SIZE);
  740. WinAssert(buffer);
  741. do
  742. {
  743. memcpy(buffer, pbuf, count);
  744. if ((p + count) >= size)
  745.    {
  746.    count = size - p;
  747.    p = size;
  748.    }
  749. else
  750.    {
  751.    p += count;
  752.    }
  753. pbuf += count;
  754. buffer[count] = '\0';
  755. fprintf(file, "%s", buffer);
  756. } while (p < size);
  757. free(buffer);
  758. return size;
  759. }
  760.  
  761. #ifdef __BORLANDC__
  762. #pragma argsused
  763. #endif
  764.  
  765. /* fprintf clone for code in unzip.c, etc. */
  766. int __far __cdecl fprintf(FILE *file, const char *format, ...)
  767. {
  768. va_list argptr;
  769. HANDLE hMemory;
  770. PSTR pszBuffer;
  771. unsigned int len;
  772.  
  773. va_start(argptr, format);
  774. hMemory = LocalAlloc(LMEM_MOVEABLE, (FPRINTF_BUF_SIZE + STDIO_BUF_SIZE));
  775. WinAssert(hMemory);
  776. if (!hMemory)
  777.    {
  778.    return FALSE;
  779.    }
  780. pszBuffer = (PSTR)LocalLock(hMemory);
  781. WinAssert(pszBuffer);
  782. len = vsprintf(pszBuffer, format, argptr);
  783. va_end(argptr);
  784. WinAssert(strlen(pszBuffer) < (FPRINTF_BUF_SIZE + STDIO_BUF_SIZE));  /* raise STDIO_BUF_SIZE ?   */
  785. MapCRtoCRLF(pszBuffer);   /*   map CR's to CRLF's */
  786. WriteStringToMsgWin(pszBuffer, bRealTimeMsgUpdate);
  787. LocalUnlock(hMemory);
  788. LocalFree(hMemory);
  789. /* MW: changed to return the actual number of bytes being printed */
  790. return len;
  791. }
  792.  
  793. void __far __cdecl perror(const char *parm1)
  794. {
  795. printf(parm1);
  796. }
  797.  
  798. /* Copy contents of status window to clipboard.  Called on receipt of IDM_COPY message.
  799.  */
  800. void CopyStatusToClipboard(HWND hWnd)
  801. {
  802. HANDLE hGlobalMemory; /* memory handles */
  803. LPSTR  lpGlobalMemory, lpStatusBuffer ; /* memory pointers */
  804. DWORD  dwTextSize = dwStatusSize;
  805.  
  806. if (!StatusInWindow())
  807.    return;
  808.  
  809. if (dwTextSize > 0x10000L - 2)
  810.    dwTextSize = 0x10000L - 2;
  811.  
  812. if ((hGlobalMemory = GlobalAlloc(GHND, dwTextSize+1L))== 0)
  813.    {
  814.    MessageBox (hWnd, szCantClipboard,
  815.       "Error", MB_ICONEXCLAMATION | MB_OK);
  816.    }
  817. else /* Got it! */
  818.    {
  819.    lpGlobalMemory = GlobalLock(hGlobalMemory);
  820.    WinAssert(lpGlobalMemory);   /* DEBUG */
  821.    lpStatusBuffer = GlobalLock(hStatusBuffer);
  822.    WinAssert(lpStatusBuffer);   /* DEBUG */
  823.    memcpy((void __far *)lpGlobalMemory,
  824.       (void __far *)lpStatusBuffer, (size_t)dwTextSize);
  825.    lpGlobalMemory[(int)dwTextSize] = '\0'; /* CF_TEXT requires null */
  826.    GlobalUnlock(hStatusBuffer);   /* unlock status buffer */
  827.    GlobalUnlock(hGlobalMemory);
  828.    OpenClipboard(hWnd);
  829.    EmptyClipboard();
  830.    SetClipboardData(CF_TEXT, hGlobalMemory);
  831.    CloseClipboard();
  832.    }
  833. }
  834.  
  835. /* Returns TRUE if Status window has anything in it; i.e. is non-clear.
  836.  */
  837. BOOL StatusInWindow(void)
  838. {
  839. return((BOOL)dwStatusSize);
  840. }
  841.  
  842.